{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1.6. Creating a simple kernel for Jupyter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:01.341618Z",
     "start_time": "2023-10-20T12:45:01.292357Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing plotkernel.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile plotkernel.py\n",
    "\n",
    "from ipykernel.kernelbase import Kernel\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from io import BytesIO\n",
    "import urllib, base64"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:01.342111Z",
     "start_time": "2023-10-20T12:45:01.303692Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Appending to plotkernel.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile plotkernel.py -a\n",
    "\n",
    "def _to_png(fig):\n",
    "    \"\"\"Return a base64-encoded PNG from a\n",
    "    matplotlib figure.\"\"\"\n",
    "    imgdata = BytesIO()\n",
    "    fig.savefig(imgdata, format='png')\n",
    "    imgdata.seek(0)\n",
    "    return urllib.parse.quote(\n",
    "        base64.b64encode(imgdata.getvalue()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:01.342399Z",
     "start_time": "2023-10-20T12:45:01.317610Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Appending to plotkernel.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile plotkernel.py -a\n",
    "\n",
    "_numpy_namespace = {n: getattr(np, n)\n",
    "                    for n in dir(np)}\n",
    "def _parse_function(code):\n",
    "    \"\"\"Return a NumPy function from a\n",
    "    string 'y=f(x)'.\"\"\"\n",
    "    return lambda x: eval(code.split('=')[1].strip(),\n",
    "                          _numpy_namespace, {'x': x})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:01.342546Z",
     "start_time": "2023-10-20T12:45:01.324036Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Appending to plotkernel.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile plotkernel.py -a\n",
    "\n",
    "class PlotKernel(Kernel):\n",
    "    implementation = 'Plot'\n",
    "    implementation_version = '1.0'\n",
    "    language = 'python'  # will be used for\n",
    "                         # syntax highlighting\n",
    "    language_version = '3.6'\n",
    "    language_info = {'name': 'plotter',\n",
    "                     'mimetype': 'text/plain',\n",
    "                     'extension': '.py'}\n",
    "    banner = \"Simple plotting\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:01.343674Z",
     "start_time": "2023-10-20T12:45:01.336851Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Appending to plotkernel.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile plotkernel.py -a\n",
    "\n",
    "    def do_execute(self, code, silent,\n",
    "                   store_history=True,\n",
    "                   user_expressions=None,\n",
    "                   allow_stdin=False):\n",
    "\n",
    "        # We create the plot with matplotlib.\n",
    "        fig, ax = plt.subplots(1, 1, figsize=(6,4),\n",
    "                               dpi=100)\n",
    "        x = np.linspace(-5., 5., 200)\n",
    "        functions = code.split('\\n')\n",
    "        for fun in functions:\n",
    "            f = _parse_function(fun)\n",
    "            y = f(x)\n",
    "            ax.plot(x, y)\n",
    "        ax.set_xlim(-5, 5)\n",
    "\n",
    "        # We create a PNG out of this plot.\n",
    "        png = _to_png(fig)\n",
    "\n",
    "        if not silent:\n",
    "            # We send the standard output to the\n",
    "            # client.\n",
    "            self.send_response(\n",
    "                self.iopub_socket,\n",
    "                'stream', {\n",
    "                    'name': 'stdout',\n",
    "                    'data': ('Plotting {n} '\n",
    "                             'function(s)'). \\\n",
    "                            format(n=len(functions))})\n",
    "\n",
    "            # We prepare the response with our rich\n",
    "            # data (the plot).\n",
    "            content = {\n",
    "                'source': 'kernel',\n",
    "\n",
    "                # This dictionary may contain\n",
    "                # different MIME representations of\n",
    "                # the output.\n",
    "                'data': {\n",
    "                    'image/png': png\n",
    "                },\n",
    "\n",
    "                # We can specify the image size\n",
    "                # in the metadata field.\n",
    "                'metadata' : {\n",
    "                      'image/png' : {\n",
    "                        'width': 600,\n",
    "                        'height': 400\n",
    "                      }\n",
    "                    }\n",
    "            }\n",
    "\n",
    "            # We send the display_data message with\n",
    "            # the contents.\n",
    "            self.send_response(self.iopub_socket,\n",
    "                'display_data', content)\n",
    "\n",
    "        # We return the exection results.\n",
    "        return {'status': 'ok',\n",
    "                'execution_count':\n",
    "                    self.execution_count,\n",
    "                'payload': [],\n",
    "                'user_expressions': {},\n",
    "               }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:01.359153Z",
     "start_time": "2023-10-20T12:45:01.343025Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Appending to plotkernel.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile plotkernel.py -a\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    from ipykernel.kernelapp import IPKernelApp\n",
    "    IPKernelApp.launch_instance(\n",
    "        kernel_class=PlotKernel)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:01.470941Z",
     "start_time": "2023-10-20T12:45:01.348005Z"
    }
   },
   "outputs": [],
   "source": [
    "%mkdir -p plotter/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:01.501445Z",
     "start_time": "2023-10-20T12:45:01.471996Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing plotter/kernel.json\n"
     ]
    }
   ],
   "source": [
    "%%writefile plotter/kernel.json\n",
    "{\n",
    " \"argv\": [\"python\", \"-m\",\n",
    "          \"plotkernel\", \"-f\",\n",
    "          \"{connection_file}\"],\n",
    " \"display_name\": \"Plotter\",\n",
    " \"name\": \"Plotter\",\n",
    " \"language\": \"python\"\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:02.700406Z",
     "start_time": "2023-10-20T12:45:01.478432Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[InstallKernelSpec] Installed kernelspec plotter in /Users/jarvis/Library/Jupyter/kernels/plotter\r\n"
     ]
    }
   ],
   "source": [
    "!jupyter kernelspec install --user plotter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-10-20T12:45:03.225510Z",
     "start_time": "2023-10-20T12:45:02.701443Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Available kernels:\r\n",
      "  plotter    /Users/jarvis/Library/Jupyter/kernels/plotter\r\n",
      "  python3    /opt/anaconda3/envs/py309/share/jupyter/kernels/python3\r\n"
     ]
    }
   ],
   "source": [
    "!jupyter kernelspec list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "def do_complete(self, code, cursor_pos):\n",
    "    return {'status': 'ok',\n",
    "            'cursor_start': ...,\n",
    "            'cursor_end': ...,\n",
    "            'matches': [...]}\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "language": "python",
   "display_name": "Python 3 (ipykernel)"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
